import { HttpException, HttpStatus, Injectable } from '@nestjs/common';
import { defer, map, of, catchError, concatMap, tap, } from 'rxjs';
import axios, { RawAxiosRequestHeaders } from 'axios';
import * as fs from "fs-extra"
import { BiliUploadTask } from '@/modules/bili/bili.typing';
import IHelper from '@/services/helper.service';
import ILogger from '@/services/logger.service';

@Injectable()
export class BiliService {
  private readonly UPLOAD_PROFILE: string = "ugcupos/bup"
  private readonly CSRF_TOKEN: string = "de3c9b2be713519c2780b6605b1695ed"
  private readonly MEMBER_COOKIE: string = "buvid3=2D7F9E2D-B89F-41D5-9686-03B8B4616FD2167621infoc; i-wanna-go-back=-1; LIVE_BUVID=AUTO8516415659004109; buvid4=9CD7940A-55F6-99D4-6CD2-F81274DE45BB49188-022012117-y7FzQHcGAvocTX3y3ZTAeQ%3D%3D; buvid_fp_plain=undefined; CURRENT_BLACKGAP=0; is-2022-channel=1; header_theme_version=CLOSE; hit-new-style-dyn=1; CURRENT_FNVAL=4048; CURRENT_PID=00672e10-cd16-11ed-9f1d-a54d650c8c4a; FEED_LIVE_VERSION=V8; nostalgia_conf=-1; DedeUserID=404186078; DedeUserID__ckMd5=f3b1debd6c153fcf; SESSDATA=e9c488fa%2C1712193871%2C467a8%2Aa2CjDlG2jIMeohteWDlu46bsLBiCNpbNUEJEdOZkO7DdFruoTWxoL1YjiAfmzqjOOV9_oSVmJnc01xS3VIMTFRVTVHNXNLeTl6eVZfTDhwU3Q5aGFQd0g0eXBqN1NianRVZi02VC1YYUxFbE05aERkZ3haUEZyTm0wN0NIdGtCc0FFRGxpSDk0RWVBIIEC; bili_jct=de3c9b2be713519c2780b6605b1695ed; CURRENT_QUALITY=80; enable_web_push=DISABLE; b_nut=100; _uuid=371107714-9319-7EB5-E64A-5109757CFA84231790infoc; hit-dyn-v2=1; fingerprint=0da12c8c0755baf4000e8043773052e5; rpdid=|(J~J|lJ)kuR0J'u~|JuJlJku; sid=5l80236u; home_feed_column=5; browser_resolution=1920-853; bili_ticket=eyJhbGciOiJIUzI1NiIsImtpZCI6InMwMyIsInR5cCI6IkpXVCJ9.eyJleHAiOjE3MDUyMDg5ODMsImlhdCI6MTcwNDk0OTcyMywicGx0IjotMX0.v18Zo397ANi2NVC6CWxAGRI149InKHgZsu5zQ9mKrSQ; bili_ticket_expires=1705208923; PVID=3; bsource=search_google; buvid_fp=0da12c8c0755baf4000e8043773052e5; b_lsid=82C326B7_18CFD22C49A; bp_video_offset_404186078=885691787676483632"
  publish(opt: {videoFile: Express.Multer.File, coverFile: Express.Multer.File, config: any}) {
    const task: BiliUploadTask = {
      videoFile: opt.videoFile, coverFile: opt.coverFile, config: opt.config,
    }
    return this.uploadVideo_preUpload(task).pipe(
      concatMap(() => this.uploadVideo_getUploadId(task)),
      concatMap(() => this.uploadVideo_uploadPreChuck(task)),
      concatMap(() => this.uploadVideo_checkStatus(task)),
      concatMap(() => this.uploadCover(task)),
      concatMap(() => this.publishVideo(task)),
      map(() => {
        const {videoFile, coverFile, ...rest} = task
        return rest
      }),
    )
  }

  private uploadVideo_preUpload(task: BiliUploadTask) {
    const params = {
      'probe_version': '20221109',
      'upcdn': 'bldsa',
      'zone': 'cs',
      'name': task.videoFile.originalname,
      'r': 'upos',
      'profile': this.UPLOAD_PROFILE,
      'ssl': '0',
      'version': '2.14.0.0',
      'build': '2140000',
      'size': task.videoFile.size.toString(),
      'webVersion': '2.14.0'
    }
    const uri: string = `https://member.bilibili.com/preupload?${IHelper.convertToQueryString(params)}`
    const headers: RawAxiosRequestHeaders = {
      'Host': 'member.bilibili.com',
      'Cookie': this.MEMBER_COOKIE,
      'cache-control': 'max-age=0',
      'sec-ch-ua': '"Not_A Brand";v="8", "Chromium";v="120", "Google Chrome";v="120"',
      'sec-ch-ua-mobile': '?0',
      'sec-ch-ua-platform': '"macOS"',
      'upgrade-insecure-requests': '1',
      'user-agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36',
      'accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7',
      'sec-fetch-site': 'none',
      'sec-fetch-mode': 'navigate',
      'sec-fetch-user': '?1',
      'sec-fetch-dest': 'document',
      'accept-language': 'zh-CN,zh;q=0.9',
      'referer': 'https://www.bilibili.com/'
    }
    return defer(() =>
      axios.get(uri, { headers })
    ).pipe(
      map(d => {
        const upos_uri = d.data?.upos_uri
        if (!upos_uri) {
          throw new HttpException(`Get upos uri failed.`, HttpStatus.BAD_REQUEST)
        }
        task.res_preUpload = d.data
        return task
      })
    )
  }

  private uploadVideo_getUploadId(task: BiliUploadTask) {
    const upOsUri = this.getTaskUpOsUri(task)
    const upOsAuth: string = this.getTaskUpOsAuth(task)
    const uri = `https://upos-cs-upcdnbldsa.bilivideo.com/${upOsUri}?uploads&output=json`
    return defer(() =>
      axios.post(uri,'', {
          headers: {
            'Host': 'upos-cs-upcdnbldsa.bilivideo.com',
            'Pragma': 'no-cache',
            'Cache-Control': 'no-cache',
            'sec-ch-ua': '"Not_A Brand";v="8", "Chromium";v="120", "Google Chrome";v="120"',
            'X-Upos-Auth': upOsAuth,
            'sec-ch-ua-mobile': '?0',
            'User-Agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36',
            'sec-ch-ua-platform': '"macOS"',
            'Accept': '*/*',
            'Origin': 'https://member.bilibili.com',
            'Sec-Fetch-Site': 'cross-site',
            'Sec-Fetch-Mode': 'cors',
            'Sec-Fetch-Dest': 'empty',
            'Referer': 'https://www.bilibili.com/',
            'Accept-Language': 'zh-CN,zh;q=0.9',
            'Content-Type': 'application/x-www-form-urlencoded'
          }
        }
      )
    ).pipe(
      map(d => {
        if (!d.data?.upload_id) {
          throw new HttpException(`Get upload id failed.`, HttpStatus.BAD_REQUEST)
        }
        task.res_getUploadId = d.data
        return task
      }),
    )
  }

  private uploadVideo_uploadPreChuck(task: BiliUploadTask) {
    const upOsUri = this.getTaskUpOsUri(task)
    const uploadId: string = this.getTaskUploadId(task)
    const upOsAuth: string = this.getTaskUpOsAuth(task)
    const uri = `https://upos-cs-upcdnbldsa.bilivideo.com/${upOsUri}`
    return defer(() =>
      axios.put(uri, task.videoFile.buffer, {
          params: {
            'partNumber': '1',
            'uploadId': uploadId,
            'chunk': '0',
            'chunks': '1',
            'size': task.videoFile.size,
            'start': '0',
            'end': task.videoFile.size,
            'total': task.videoFile.size
          },
          headers: {
            'Host': 'upos-cs-upcdnbldsa.bilivideo.com',
            'Pragma': 'no-cache',
            'Cache-Control': 'no-cache',
            'sec-ch-ua': '"Not_A Brand";v="8", "Chromium";v="120", "Google Chrome";v="120"',
            'Content-Type': 'application/octet-stream',
            'X-Upos-Auth': upOsAuth,
            'sec-ch-ua-mobile': '?0',
            'User-Agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36',
            'sec-ch-ua-platform': '"macOS"',
            'Accept': '*/*',
            'Origin': 'https://member.bilibili.com',
            'Sec-Fetch-Site': 'cross-site',
            'Sec-Fetch-Mode': 'cors',
            'Sec-Fetch-Dest': 'empty',
            'Referer': 'https://www.bilibili.com/',
            'Accept-Language': 'zh-CN,zh;q=0.9'
          }
        }
      )
    ).pipe(
      map(d => {
        task.res_uploadPreChuck = d.data
        return task
      })
    )
  }

  private uploadVideo_checkStatus(task: BiliUploadTask) {
    const upOsUri: string = this.getTaskUpOsUri(task)
    const uploadId: string = this.getTaskUploadId(task)
    const upOsAuth: string = this.getTaskUpOsAuth(task)
    const bizId: string = this.getTaskBizId(task)
    const uri: string = `https://upos-cs-upcdnbldsa.bilivideo.com/${upOsUri}`
    const data = {
      'parts': [
        {
          'partNumber': 1,
          'eTag': 'etag'
        }
      ]
    }
    const params = {
      'output': 'json',
      'name': task.videoFile.originalname,
      'profile': this.UPLOAD_PROFILE,
      'uploadId': uploadId,
      'biz_id': bizId
    }
    const headers: RawAxiosRequestHeaders = {
      'Host': 'upos-cs-upcdnbldsa.bilivideo.com',
      'sec-ch-ua': '"Not_A Brand";v="8", "Chromium";v="120", "Google Chrome";v="120"',
      'Content-Type': 'application/json; charset=UTF-8',
      'X-Upos-Auth': upOsAuth,
      'sec-ch-ua-mobile': '?0',
      'User-Agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36',
      'sec-ch-ua-platform': '"macOS"',
      'Accept': '*/*',
      'Origin': 'https://member.bilibili.com',
      'Sec-Fetch-Site': 'cross-site',
      'Sec-Fetch-Mode': 'cors',
      'Sec-Fetch-Dest': 'empty',
      'Referer': 'https://www.bilibili.com/',
      'Accept-Language': 'zh-CN,zh;q=0.9'
    }
    return defer(() =>
      axios.post(uri, data, {params, headers})
    ).pipe(
      map(d => {
        if (d.data?.OK !== 1 || !d.data?.key) {
          throw new HttpException(`Check upload status failed.`, HttpStatus.BAD_REQUEST)
        }
        task.res_checkStatus = d.data
        return task
      })
    )
  }

  private uploadCover(task: BiliUploadTask) {
    const uri: string = 'https://member.bilibili.com/x/vu/web/cover/up'
    const data = new URLSearchParams({
      'cover': `data:image/png;base64,${task.coverFile.buffer.toString('base64')}`,
      'csrf': this.CSRF_TOKEN
    })
    const params = {
      't': Date.now().toString()
    }
    const headers: RawAxiosRequestHeaders = {
      'Host': 'member.bilibili.com',
      'Cookie': this.MEMBER_COOKIE,
      'pragma': 'no-cache',
      'cache-control': 'no-cache',
      'sec-ch-ua': '"Not_A Brand";v="8", "Chromium";v="120", "Google Chrome";v="120"',
      'accept': 'application/json, text/plain, */*',
      'sec-ch-ua-mobile': '?0',
      'user-agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36',
      'sec-ch-ua-platform': '"macOS"',
      'origin': 'https://member.bilibili.com',
      'sec-fetch-site': 'same-origin',
      'sec-fetch-mode': 'cors',
      'sec-fetch-dest': 'empty',
      'referer': 'https://www.bilibili.com/',
      'accept-language': 'zh-CN,zh;q=0.9'
    }
    return defer(() =>
      axios.post(uri, data, {params, headers})
    ).pipe(
      map(res => {
        if (!res.data?.data?.url) {
          throw new HttpException(`Upload cover failed.`, HttpStatus.BAD_REQUEST)
        }
        task.res_uploadCover = res.data
        return task
      })
    )
  }

  getTaskUpOsUri(task: BiliUploadTask): string {
    return task.res_preUpload.upos_uri.replace("upos://", "")
  }

  getTaskUploadId(task: BiliUploadTask): string {
    return task.res_getUploadId.upload_id
  }

  getTaskUpOsAuth(task: BiliUploadTask): string {
    return task.res_preUpload.auth
  }

  getTaskBizId(task: BiliUploadTask): string {
    return task.res_preUpload.biz_id
  }

  private publishVideo(task: BiliUploadTask) {
    const uri: string = 'https://member.bilibili.com/x/vu/web/add/v3'
    const upOsUri: string = this.getTaskUpOsUri(task)
    const bizId: string = this.getTaskBizId(task)
    const postData = {
      cover: task.res_uploadCover.data.url,
      title: task.config.title,
      copyright: 1,
      tid: +task.config.tid,
      tag: task.config.tag,
      'desc_format_id': 0,
      'desc': '',
      'recreate': -1,
      'dynamic': '',
      'interactive': 0,
      videos: [
        {
          'filename': upOsUri.split("/").pop().split(".")[0],
          'title': task.videoFile.originalname,
          'desc': '',
          'cid': bizId
        }
      ],
      'act_reserve_create': 0,
      'no_disturbance': 0,
      'no_reprint': 1,
      'subtitle': {
        'open': 0,
        'lan': ''
      },
      'dolby': 0,
      'lossless_music': 0,
      'up_selection_reply': false,
      'up_close_reply': false,
      'up_close_danmu': false,
      'web_os': 2,
      'csrf': this.CSRF_TOKEN
    }
    const params = {
      't': Date.now().toString(),
      'csrf': this.CSRF_TOKEN
    }
    ILogger.info(JSON.stringify(postData))
    const headers: RawAxiosRequestHeaders = {
      'Host': 'member.bilibili.com',
      'Cookie': this.MEMBER_COOKIE,
      'pragma': 'no-cache',
      'cache-control': 'no-cache',
      'sec-ch-ua': '"Not_A Brand";v="8", "Chromium";v="120", "Google Chrome";v="120"',
      'accept': 'application/json, text/plain, */*',
      'content-type': 'application/json;charset=UTF-8',
      'sec-ch-ua-mobile': '?0',
      'user-agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36',
      'sec-ch-ua-platform': '"macOS"',
      'origin': 'https://member.bilibili.com',
      'sec-fetch-site': 'same-origin',
      'sec-fetch-mode': 'cors',
      'sec-fetch-dest': 'empty',
      'referer': 'https://www.bilibili.com/',
      'accept-language': 'zh-CN,zh;q=0.9'
    }
    return defer(() =>
      axios.post(uri, postData,{params,headers})
    ).pipe(
      map(d => {
        task.res_publishVideo = d.data
        return task
      })
    )
  }

  getTypeList() {
    const uri: string = `https://member.bilibili.com/x/vupre/web/archive/pre`
    const params = {
      'lang': 'cn',
      't': Date.now().toString()
    }
    const headers: RawAxiosRequestHeaders = {
      'authority': 'member.bilibili.com',
      'accept': 'application/json, text/plain, */*',
      'accept-language': 'zh-CN,zh;q=0.9',
      'cache-control': 'no-cache',
      'cookie': this.MEMBER_COOKIE,
      'pragma': 'no-cache',
      'referer': 'https://www.bilibili.com/',
      'sec-ch-ua': '"Not_A Brand";v="8", "Chromium";v="120", "Google Chrome";v="120"',
      'sec-ch-ua-mobile': '?0',
      'sec-ch-ua-platform': '"macOS"',
      'sec-fetch-dest': 'empty',
      'sec-fetch-mode': 'cors',
      'sec-fetch-site': 'same-origin',
      'user-agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36'
    }
    return defer(() =>
      axios.get(uri, {params,headers})
    ).pipe(
      map(d => d.data.data.typelist)
    )
  }
}